﻿using System.Collections.Concurrent;
using Volo.Abp.DependencyInjection;

namespace FlyingEye.SignalRHubs
{
    public class SignalRGroupMapping : ISingletonDependency
    {
        private readonly ConcurrentDictionary<string, ConcurrentBag<string>> _groupToConnections =
            new ConcurrentDictionary<string, ConcurrentBag<string>>();

        private readonly ConcurrentDictionary<string, string> _connectionToGroup =
            new ConcurrentDictionary<string, string>();

        public bool TryAdd(string groupName, string connectionId)
        {
            // 添加反向映射
            if (!_connectionToGroup.TryAdd(connectionId, groupName))
                return false;

            // 添加正向映射（原子操作）
            _groupToConnections.AddOrUpdate(
                groupName,
                _ => new ConcurrentBag<string> { connectionId },
                (_, bag) => { bag.Add(connectionId); return bag; }
            );
            return true;
        }

        public bool HasGroup(string groupName)
        {
            return _groupToConnections.ContainsKey(groupName);
        }

        public bool TryRemoveByConnectionId(string connectionId, out string groupName)
        {
            // 通过反向词典直接定位组
            if (!_connectionToGroup.TryRemove(connectionId, out groupName!))
                return false;

            // 原子操作更新正向映射
            _groupToConnections.AddOrUpdate(
                groupName,
                _ => new ConcurrentBag<string>(),
                (_, bag) =>
                {
                    // 从组中移除连接（ConcurrentBag 不支持直接移除，需重建）
                    // bag.Except(new[] { connectionId }) 会对原始集合 bag 进行遍历，​
                    // 生成一个不包含 connectionId 的新集合。这一步只是逻辑上的过滤，不会修改原集合
                    var newBag = new ConcurrentBag<string>(bag.Except(new[] { connectionId }));
                    return newBag;
                }
            );

            // 若组为空则清理
            if (_groupToConnections[groupName].IsEmpty)
                _groupToConnections.TryRemove(groupName, out _);

            return true;
        }
    }
}
